from app.models import Checkout
from google.appengine.ext import db
from gdata.docs.service import DocsService, DocumentQuery
import gdata.docs
import gdata
import StringIO
import uuid
import csv
import time
import httplib
import logging

DEFAULT_NUM_ROWS = 25

# Control timeout length
import socket

def checkout_new_spreadsheet(job):
  """ Checks out a new spreadsheet 
  
  Specifically:
  (1) Uploads a spreadsheet with the appropriate file to Google Docs.
  (2) Shares spreadsheet with user
  (3) Updates checkout in datastore
  
  """
  headers, references = get_fields(job)
  num_rows = DEFAULT_NUM_ROWS
  empty_file = create_empty_file(num_rows, *headers)
  
  old_timeout = socket.getdefaulttimeout()
  
  for i in range(5): # Random 'trix.' errors at sharing stage sometimes, retry
    client = get_client(job)
    try:
      # Shorter timeouts here to avoid hanging
      socket.setdefaulttimeout(15)
      
      entry = upload_new_spreadsheet(client, empty_file)
      if not entry: continue
      
      key = entry.id.text.replace(
        'http://docs.google.com/feeds/documents/private/full/spreadsheet%3A',
        '')
      if key.startswith('trix.'): continue
      
      share_spreadsheet(client, entry, job.checked_out_to)
      
      checkout = Checkout(gdoc_key = key,
        checked_out_to = job.checked_out_to,
        fields_list = references)
      checkout.put()
      
      return '$APP_ENGINE_URL/checkout/edit?key=%s' % checkout.key()
    
    except gdata.service.RequestError:
      pass # Probably error at share stage
    
    except (httplib.BadStatusLine, socket.timeout):
      time.sleep(2) # Probably error at upload stage
      
    finally: # Restore to longer timeout
      socket.setdefaulttimeout(old_timeout)
    

def get_client(job):
  """ Returns a DocsService client, authenticates """
  client = DocsService()
  client.ClientLogin(job.gdocs_user, job.gdocs_password)
  return client
  
def get_fields(job):
  headers = []
  references = []
  index = 1
  for fieldset in db.get(job.fieldsets):
    for field in fieldset.fields_list:
      headers.append("n%s. %s" % (index, field.verbose_name))
      references.append("%s#%s" % (fieldset.key(), field.name))
      index += 1
  
  return headers, references

def create_empty_file(num_rows, *column_headers):
  """ Creates a virtual CSV file with the given rows and column headers """
  virtual_csv_file = StringIO.StringIO()
  writer = csv.writer(virtual_csv_file, lineterminator='\n')
  writer.writerow(column_headers)
  empty_row = ['' for i in range(len(column_headers))]
  for i in range(num_rows):
    writer.writerow(empty_row)
  
  virtual_csv_file.seek(0)
  length = len(virtual_csv_file.read())
  virtual_csv_file.__len__ = (lambda : length)
    
  virtual_csv_file.seek(0)
  return virtual_csv_file

def upload_new_spreadsheet(client, spreadsheet):
  """ Uploads a new blank spreadsheet to Google Docs """
  spreadsheet.seek(0)
  
  for i in range(3): # Try this 3 times before quitting
    name = str(uuid.uuid4())
    spreadsheet.seek(0)
    virtual_media_source = gdata.MediaSource(file_handle=spreadsheet,
        content_type='text/csv', content_length=len(spreadsheet))
    try:
      client.UploadSpreadsheet(virtual_media_source, name)
    except (gdata.service.RequestError, httplib.BadStatusLine, socket.timeout), x:
      pass # Something is rotten in the state of Google's Data APIs
      
    # Now find by the name we gave it
    title_query = gdata.docs.service.DocumentQuery()
    title_query['title'] = name
    
    for j in range(2): # Try this 2 times before quitting
      try:
        feed = client.QueryDocumentListFeed(title_query.ToUri())
        return feed.entry[0]
      except (IndexError, gdata.service.RequestError, httplib.BadStatusLine, socket.timeout), x:
        time.sleep(1)
        pass # Not found, let's try again -- maybe it'll pop up later

def share_spreadsheet(client, entry, user):
  """ Gives user designated user rights to access given spreadsheet entry """
  scope = gdata.docs.Scope(value=user.email(), type='user')
  role = gdata.docs.Role(value='writer')
  acl_entry = gdata.docs.DocumentListAclEntry(scope=scope, role=role)
  acl_href = entry.GetAclLink().href
  # acl_href = acl_href.replace('trix.','')
  client.Post(acl_entry, acl_href,
    converter=gdata.docs.DocumentListAclEntryFromString)
